package com.heima.comment.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.aliyun.AliyunGreenTemplate;
import com.heima.comment.model.dto.CommentSaveDto;
import com.heima.comment.model.entity.ApComment;
import com.heima.comment.model.entity.ApCommentLike;
import com.heima.comment.model.vo.ApCommentVo;
import com.heima.comment.service.CommentService;
import com.heima.comment.utils.AppUserThreadLocalUtil;
import com.heima.common.constants.CommentConstants;
import com.heima.common.constants.KafkaMessageConstants;
import com.heima.feign.user.UserFeignClient;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.message.UpdateArticleMess;
import com.heima.model.user.entity.ApUser;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CommentServiceImpl implements CommentService {

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private UserFeignClient userFeignClient;

    @Resource
    private AliyunGreenTemplate aliyunGreenTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private KafkaTemplate kafkaTemplate;

    /**
     * 发布评论
     */
    @Override
    public ResponseResult save(String targetId, String content, Short commentType) {
        //1.参数校验
        if (targetId == null || content == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (content.length() > 140) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "评论内容不能超过140字");
        }
        //2.判断是否登录
        ApUser user = AppUserThreadLocalUtil.get();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //3.获取用户信息
        //3.1先从redis中获取
        String user_json = stringRedisTemplate.opsForValue().get("LOGIN_USER_" + user.getId());
        //如果存在
        if (StringUtils.isNotBlank(user_json)) {
            user = JSON.parseObject(user_json, ApUser.class);
        } else {
            //3.2如果没有查询数据库  --feign远程调用   再存入redis并设置过期时间
            ResponseResult<ApUser> result = userFeignClient.findUserById(user.getId());
            if (result.getCode() == 200 && result.getData() != null) {
                user = result.getData();
                user_json = JSON.toJSONString(user);
                //存入redis并设置过期时间
                stringRedisTemplate.opsForValue().set("LOGIN_USER_" + user.getId(), user_json, 1, TimeUnit.DAYS);
            } else {
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "当前登录信息有误");
            }
        }

        //4.审核内容
        Map<String, String> textScan = aliyunGreenTemplate.greenTextScan(content);
        if (!"pass".equals(textScan.get("suggestion"))) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "评论信息违规");
        }
        //5.封装实体信息，保存到mongodb数据库
        ApComment apComment = new ApComment();
        apComment.setId(ObjectId.get().toHexString());
        apComment.setUserId(user.getId());
        apComment.setNickName(user.getNickname());
        apComment.setContent(content);
        apComment.setTargetId(targetId);
        apComment.setCreatedTime(new Date());
        apComment.setUpdatedTime(new Date());
        apComment.setImage(user.getImage());
        apComment.setLikes(0);
        apComment.setReply(0);
        mongoTemplate.save(apComment);

        //如果是评论回复，需要更新评论数量
        if (CommentConstants.COMMENT_TARGET_COMMENT.equals(commentType)) {
            Query query = Query.query(Criteria.where("id").is(targetId));
            UpdateDefinition update = new Update().inc("reply", 1);
            mongoTemplate.updateFirst(query, update, ApComment.class);
        }

        //评论文章的时候需要发送文章的评论行为消息
        if (CommentConstants.COMMENT_TARGET_ARTICLE.equals(commentType)) {
            //发送行为消息
            UpdateArticleMess mess = new UpdateArticleMess();
            mess.setArticleId(Long.valueOf(targetId));
            mess.setType(UpdateArticleMess.UpdateArticleType.COMMENT);
            mess.setAdd(1);
            kafkaTemplate.send(KafkaMessageConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 评论点赞
     *
     * @param targetId
     * @param operation
     * @return
     */
    @Override
    public ResponseResult like(String targetId, Short operation) {
        //1.参数校验
        if (StringUtils.isEmpty(targetId)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        ApUser user = AppUserThreadLocalUtil.get();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //判断要点赞的评论是否存在
        ApComment comment = mongoTemplate.findById(targetId, ApComment.class);
        if (comment == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //2.判断用户是否已经点赞
        String comment_like_key = "COMMENT_LIKE_" + user.getId();
        Boolean isMember = stringRedisTemplate.opsForSet().isMember(comment_like_key, targetId);
        //2.1已经点赞 --点赞
        if (isMember && operation == 0) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }
        //2.2已经点赞 --取消点赞
        if (isMember && operation == 1) {
            //删除redis缓存
            stringRedisTemplate.opsForSet().remove(comment_like_key, targetId);
            //删除mongodb数据
            mongoTemplate.remove(Query.query(Criteria.where("userId").is(user.getId()).and("targetId").is(targetId)), ApCommentLike.class);

            //扣减评论集合中的点赞数量  ---涉及到原子性操作
            Query query = Query.query(Criteria.where("id").is(targetId).and("likes").gt(0));
            UpdateDefinition update = new Update().inc("likes", -1);
            mongoTemplate.updateFirst(query, update, ApComment.class);

        }
        //2.3未点赞 --点赞
        if (!isMember && operation == 0) {
            //保存redis
            stringRedisTemplate.opsForSet().add(comment_like_key, targetId);
            //保存mongodb数据
            ApCommentLike commentLike = new ApCommentLike();
            commentLike.setId(ObjectId.get().toHexString());
            commentLike.setTargetId(targetId);
            commentLike.setUserId(user.getId());
            commentLike.setCreatedTime(new Date());
            mongoTemplate.save(commentLike);

            //增加评论集合中的点赞数量  ---涉及到原子性操作
            Query query = Query.query(Criteria.where("id").is(targetId));
            UpdateDefinition update = new Update().inc("likes", 1);
            mongoTemplate.updateFirst(query, update, ApComment.class);
        }
        //3.封装数据返回 --返回点赞数量
        comment = mongoTemplate.findById(targetId, ApComment.class);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("likes", comment.getLikes());
        return ResponseResult.okResult(resultMap);
    }

    /**
     * 评论列表展示
     *
     * @param targetId
     * @param size
     * @param minDate
     * @return
     */
    @Override
    public ResponseResult load(String targetId, Integer size, Date minDate) {
        //1.参数校验
        if (StringUtils.isEmpty(targetId)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        if (size == null || size <= 0) {
            size = 10;
        }
        size = Math.min(size, 50);

        if (minDate == null) {
            minDate = new Date();
        }
        //2.查询数据库的评论数据   分页和排序
        Query query = Query.query(Criteria.where("targetId").is(targetId).and("createdTime").lt(minDate))
                .with(Sort.by("createdTime").descending()).limit(size);
        List<ApComment> apCommentList = mongoTemplate.find(query, ApComment.class);
        //3.用户未登录，返回原始数据
        ApUser apUser = AppUserThreadLocalUtil.get();
        if (apUser == null) {
            return ResponseResult.okResult(apCommentList);
        }
        //4.用户登录，判断已点赞的评论
        //4.1去redis查询当前用户的所有评论ids
        String comment_like_key = "COMMENT_LIKE_" + apUser.getId();
        Set<String> ids = stringRedisTemplate.opsForSet().members(comment_like_key);
        //4.2为空，没有点赞数据，返回原始数据
        if (ids == null) {
            return ResponseResult.okResult(apCommentList);
        }
        //4.3存在点赞数据，遍历找到数据并设置operation为0，返回新的结果集
        List<ApCommentVo> list = apCommentList.stream().map(ApComment -> {
            ApCommentVo apCommentVo = new ApCommentVo();
            BeanUtils.copyProperties(ApComment, apCommentVo);
            if (ids.contains(ApComment.getId())) {
                apCommentVo.setOperation((short) 0);
            }
            return apCommentVo;
        }).collect(Collectors.toList());
        return ResponseResult.okResult(list);
    }
}
